package com.silence.gmall.template.impl;

import com.silence.gmall.config.properties.MinioProperties;
import com.silence.gmall.enums.CommonCode;
import com.silence.gmall.exception.ServiceException;
import com.silence.gmall.common.vo.res.FileUploadResVo;
import com.silence.gmall.template.MinioTemplate;
import io.minio.*;
import io.minio.http.Method;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Author silence
 * @Email 792245022@qq.com
 * @Date 2025-01-21 00:00
 * @Description 文件上传服务实现类
 */
@Slf4j
@Component
@RequiredArgsConstructor
@ConditionalOnBean(MinioClient.class)
public class MinioTemplateImpl implements MinioTemplate {

    /**
     * MinIO 客户端
     */
    private final MinioClient minioClient;

    /**
     * MinIO 配置属性
     */
    private final MinioProperties minioProperties;

    /**
     * 允许的文件类型
     */
    private static final String[] ALLOWED_EXTENSIONS = {
            // 图片
            ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp",
            // 文档
            ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx",
            // 其他
            ".txt", ".zip", ".rar", ".7z",
            // 视频
            ".mp4", ".avi", ".mov", ".wmv", ".flv", ".webm"
    };

    /**
     * 最大文件大小（50MB）
     */
    private static final long MAX_FILE_SIZE = 52428800;

    /**
     * 上传单个文件
     *
     * @param file 文件
     * @return 上传结果
     */
    @Override
    public FileUploadResVo uploadFile(MultipartFile file) {
        return uploadFile(file, "");
    }

    /**
     * 上传单个文件到指定路径
     *
     * @param file 文件
     * @param path 存储路径（如：images/product/）
     * @return 上传结果
     */
    @Override
    public FileUploadResVo uploadFile(MultipartFile file, String path) {
        validateFile(file);

        String originalFileName = null;
        try {
            // 确保存储桶存在
            ensureBucketExists();

            // 生成文件名和路径
            originalFileName = file.getOriginalFilename();
            String extension = getFileExtension(originalFileName);
            String fileName = generateFileName() + extension;
            String objectName = buildObjectName(path, fileName);

            // 上传文件
            try (InputStream inputStream = file.getInputStream()) {
                minioClient.putObject(
                        PutObjectArgs.builder()
                                .bucket(minioProperties.getBucketName())
                                .object(objectName)
                                .stream(inputStream, file.getSize(), -1)
                                .contentType(file.getContentType())
                                .build()
                );
            }

            // 构建文件访问URL
            String fileUrl = String.format("%s/%s/%s",
                    minioProperties.getFileHost(),
                    minioProperties.getBucketName(),
                    objectName);

            log.info("文件上传成功：{} -> {}", originalFileName, objectName);

            return FileUploadResVo.builder()
                    .fileName(fileName)
                    .originalFileName(originalFileName)
                    .fileSize(file.getSize())
                    .contentType(file.getContentType())
                    .fileUrl(fileUrl)
                    .bucketName(minioProperties.getBucketName())
                    .objectName(objectName)
                    .uploadTime(System.currentTimeMillis())
                    .build();

        } catch (Exception e) {
            String errorLog = "文件上传失败：" + originalFileName;
            log.error(errorLog, e);
            throw new ServiceException(CommonCode.FAIL, "文件上传失败：" + e.getMessage());
        }
    }

    /**
     * 批量上传文件
     *
     * @param files 文件列表
     * @return 上传结果列表
     */
    @Override
    public List<FileUploadResVo> uploadFiles(List<MultipartFile> files) {
        return uploadFiles(files, "");
    }

    /**
     * 批量上传文件
     *
     * @param files 文件列表
     * @return 上传结果列表
     */
    @Override
    public List<FileUploadResVo> uploadFiles(List<MultipartFile> files, String path) {
        if (CollectionUtils.isEmpty(files)) {
            throw new ServiceException(CommonCode.FAIL, "文件列表不能为空");
        }

        List<FileUploadResVo> results = new ArrayList<>();
        for (MultipartFile file : files) {
            if (!file.isEmpty()) {
                try {
                    FileUploadResVo result = uploadFile(file, path);
                    results.add(result);
                } catch (Exception e) {
                    String errorLog = "批量上传文件失败：" + file.getOriginalFilename();
                    log.error(errorLog, e);
                    // 继续上传其他文件，不中断整个过程
                }
            }
        }

        return results;
    }

    /**
     * 删除文件
     *
     * @param objectName 对象名
     * @return 删除结果
     */
    @Override
    public boolean deleteFile(String objectName) {
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(minioProperties.getBucketName())
                            .object(objectName)
                            .build()
            );
            log.info("文件删除成功：{}", objectName);
            return true;
        } catch (Exception e) {
            String errorLog = "文件删除失败：" + objectName;
            log.error(errorLog, e);
            return false;
        }
    }

    /**
     * 获取文件预览URL
     *
     * @param objectName 对象名
     * @param expires    过期时间（秒）
     * @return 预览URL
     */
    @Override
    public String getPreviewUrl(String objectName, Integer expires) {
        try {
            if (Objects.isNull(expires) || expires <= 0) {
                // 默认7天
                expires = 7 * 24 * 60 * 60;
            }

            return minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(minioProperties.getBucketName())
                            .object(objectName)
                            .expiry(expires, TimeUnit.SECONDS)
                            .build()
            );
        } catch (Exception e) {
            String errorLog = "获取文件预览URL失败：" + objectName;
            log.error(errorLog, e);
            throw new ServiceException(CommonCode.FAIL, "获取文件预览URL失败：" + e.getMessage());
        }
    }

    /**
     * 验证文件
     */
    private void validateFile(MultipartFile file) {
        if (Objects.isNull(file) || file.isEmpty()) {
            throw new ServiceException(CommonCode.FAIL, "文件不能为空");
        }

        String originalFileName = file.getOriginalFilename();
        if (StringUtils.isBlank(originalFileName)) {
            throw new ServiceException(CommonCode.FAIL, "文件名不能为空");
        }

        // 检查文件大小
        if (file.getSize() > MAX_FILE_SIZE) {
            throw new ServiceException(CommonCode.FAIL, "文件大小不能超过50MB");
        }

        // 检查文件类型
        String extension = getFileExtension(originalFileName).toLowerCase();
        boolean isAllowed = false;
        for (String allowedExt : ALLOWED_EXTENSIONS) {
            if (allowedExt.equals(extension)) {
                isAllowed = true;
                break;
            }
        }

        if (!isAllowed) {
            throw new ServiceException(CommonCode.FAIL, "不支持的文件类型：" + extension);
        }
    }

    /**
     * 确保存储桶存在
     */
    private void ensureBucketExists() {
        try {
            boolean exists = minioClient.bucketExists(
                    BucketExistsArgs.builder()
                            .bucket(minioProperties.getBucketName())
                            .build()
            );

            if (!exists) {
                minioClient.makeBucket(
                        MakeBucketArgs.builder()
                                .bucket(minioProperties.getBucketName())
                                .build()
                );
                log.info("创建存储桶：{}", minioProperties.getBucketName());
            }
        } catch (Exception e) {
            log.error("检查/创建存储桶失败", e);
            throw new ServiceException(CommonCode.FAIL, "存储桶操作失败：" + e.getMessage());
        }
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String fileName) {
        if (StringUtils.isBlank(fileName)) {
            return "";
        }
        int lastDotIndex = fileName.lastIndexOf('.');
        return lastDotIndex > 0 ? fileName.substring(lastDotIndex) : "";
    }

    /**
     * 生成文件名
     */
    private String generateFileName() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 构建对象名（完整路径）
     */
    private String buildObjectName(String path, String fileName) {
        // 按日期分目录：2025/01/21/
        String dateDir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));

        StringBuilder objectName = new StringBuilder();

        // 添加自定义路径
        if (StringUtils.isNotBlank(path)) {
            path = path.trim();
            if (!path.endsWith("/")) {
                path += "/";
            }
            objectName.append(path);
        }

        // 添加日期目录
        objectName.append(dateDir).append("/");

        // 添加文件名
        objectName.append(fileName);

        return objectName.toString();
    }
}